package v2ray

import (
	"context"
	"encoding/json"
	"errors"
	"gitee.com/yysf-xin/dproxy/pkg/db"
	"gitee.com/yysf-xin/dproxy/pkg/model"
	"gitee.com/yysf-xin/dproxy/pkg/tools"
	"gitee.com/yysf-xin/dproxy/pkg/v2ray/clash"
	core "github.com/v2fly/v2ray-core/v5"
	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/tlscfg"
	v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4"
	"github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg"
	"unsafe"
)

func CreateHandler(ins *core.Instance, v *clash.ConfigClash) (h interface{}, err error) {
	switch v.Type {
	case "ss":
		h, err = processSS(ins, v)
	case "trojan":
		h, err = processTrojan(ins, v)
	case "vmess":
		h, err = processVmess(ins, v)
	}
	return
}

type Info struct {
	Checking int32
	Total    int
	Error    int
	Success  int32
	Stop     bool
}

type CornClashTask struct {
	CheckClashTask
	Ins *core.Instance
	Db  *db.Client
	Url string
}

func (t *CornClashTask) Run() {
	if !t.Stop {
		return
	}
	Url := t.Url
	c, err := LoadClashFromUrl(Url)
	if err != nil {
		tools.Log.Println(err)
		return
	}
	n := t.OnlyNew
	t.CheckClashTask = *c
	t.Url = Url
	t.OnlyNew = n
	t.Stop = false
	t.CheckClash(t.Ins, t.Db)
}

func processTrojan(ins *core.Instance, v *clash.ConfigClash) (interface{}, error) {
	return buildV5(ins, "trojan", &ClientConfig{
		Address:  v.Server,
		Port:     v.Port,
		Password: v.Password,
	}, func(config *v5cfg.OutboundConfig) {
		if v.TLS {
			config.StreamSetting = &v5cfg.StreamConfig{
				Security: "tls",
			}
		}
	})
}

type Model struct {
	ConfigClash  *clash.ConfigClash
	ProxyHistory *model.ProxyHistory
	ProxyInfo    *model.ProxyInfo
}

func processVmess(ins *core.Instance, v *clash.ConfigClash) (interface{}, error) {
	vmess := &VmessConfig{
		Vnext: []*VmessVnext{
			{
				Address: v.Server,
				Port:    v.Port,
				Users: []*VmessUser{
					{
						Id:       v.UUID,
						AlterId:  v.AlterId,
						Security: v.Cipher,
					},
				},
			},
		},
	}
	if v.Network == "" {
		v.Network = "tcp"
	}
	if v.Network == "tcp" {
		return build4(ins, "vmess", vmess, nil)
	} else if v.Network == "ws" || v.Network == "http" {
		return build4(ins, "vmess", vmess, func(config *v4.OutboundDetourConfig) {

			if v.ServerName != "" {
				if v.WsHeaders == nil {
					v.WsHeaders = make(map[string]string)
				}
				if len(v.WsHeaders) == 0 {
					v.WsHeaders["HOST"] = v.ServerName
				}
			}

			network := v4.TransportProtocol("ws")
			config.StreamSetting = &v4.StreamConfig{
				Network: &network,
				WSSettings: &v4.WebSocketConfig{
					Path:    v.WsPath,
					Headers: v.WsHeaders,
				},
			}
			hosts := make(cfgcommon.StringList, 1)
			hosts[0] = v.ServerName

			if v.HttpOpts != nil && len(v.HttpOpts.Path) > 0 {
				config.StreamSetting.HTTPSettings = &v4.HTTPConfig{
					Host:    &hosts,
					Path:    v.HttpOpts.Path[0],
					Method:  v.HttpOpts.Method,
					Headers: *(*map[string]*cfgcommon.StringList)(unsafe.Pointer(&v.HttpOpts.Headers)),
				}
			}

			if v.TLS {
				config.StreamSetting.Security = "tls"
				config.StreamSetting.TLSSettings = &tlscfg.TLSConfig{
					Insecure: true,
				}
			}
		})
	}
	tools.Log.Println("not support", v.Name, v.Type, v.Network)
	return nil, errors.New("not support")
}

func processSS(ins *core.Instance, v *clash.ConfigClash) (interface{}, error) {
	return buildV5(ins, "shadowsocks", &ShadowSockClient{
		ClientConfig: ClientConfig{
			Address:  v.Server,
			Port:     v.Port,
			Password: v.Password,
		},
		Method:                         v.Cipher,
		ExperimentReducedIvHeadEntropy: false,
	}, func(config *v5cfg.OutboundConfig) {
		if v.TLS {
			//println(v.Name,v.Server,v.TLS)
		}
	})
}

type CustomV4Config func(*v4.OutboundDetourConfig)

func build4(ins *core.Instance, protocol string, settings interface{}, config CustomV4Config) (interface{}, error) {
	r, err := json.Marshal(settings)
	if err != nil {
		return nil, err
	}
	//println(string(r))

	jw := json.RawMessage(r)

	cfg := &v4.OutboundDetourConfig{
		Protocol: protocol,
		Settings: &jw,
	}
	if config != nil {
		config(cfg)
	}
	msg, err := cfg.Build()
	if err != nil {
		return nil, err
	}
	return core.CreateObject(ins, msg)
}

type CustomV5Config func(*v5cfg.OutboundConfig)

func buildV5(ins *core.Instance, protocol string, settings interface{}, config CustomV5Config) (interface{}, error) {
	r, err := json.Marshal(settings)
	if err != nil {
		return nil, err
	}
	//println(string(r))
	cfg := &v5cfg.OutboundConfig{
		Protocol: protocol,
		Settings: r,
	}
	if config != nil {
		config(cfg)
	}
	msg, err := cfg.BuildV5(context.TODO())
	if err != nil {
		return nil, err
	}
	return core.CreateObject(ins, msg)
}
